#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Purpose: Import object types dialog
# Created: 03/31/2009
# $Id $
# Copyright (c) 2008,2009 by University College London
# Authors:
# Didrik Pinte --<dpinte@dipole-consulting.com>
#
# This program is free software under the GPL (>=v2)
# Read the file COPYING coming with HydroPlatform for details.
'''
Object importer dialog allows the user to import node and link types from 
another project in the same database

TODO : support importing from other databases
'''
from sqlalchemy import and_
from sqlalchemy.orm.session import Session

from enthought.traits.api import HasTraits, List, Int, Instance, DelegatesTo, \
     Dict, cached_property, Property
from enthought.traits.ui.api import View, Item, CheckListEditor, Handler, \
     Label, EnumEditor

from enthought.traits.ui.menu import OKButton, CancelButton

from Thuban.UI.context import Context

from hydroplatform.model.network import Project, NodeType, LinkType
from hydroplatform.model.networkfun import check_project_has_unit

class ButtonHandler(Handler):
    '''
    Manages what to do when the user has finished choosing the objects 
    he wants to import
    '''
    def close(self, info, is_ok):
        '''
        On close, transfer the needed link and node types 
        '''
        if is_ok:
            session = info.object.session
            project = info.object.project
            for ntypeid in info.object.ntypes:
                # get node type by its id
                ntype = session.query(NodeType).filter( \
                    NodeType.id == ntypeid).one() 
                # copy node type to project
                newtype = ntype.copy_to_project(project)
                session.add(newtype)
                for field in ntype.fields:
                    try:
                        check_project_has_unit(session, project, \
                                           field.attribute_type.unit)
                    except AttributeError, exc:
                        pass
                    newfield = field.copy_to_otype(newtype)
                    session.add(newfield)
            for ltypeid in info.object.ltypes:
                # get node type by its id
                ltype = session.query(LinkType).filter(\
                    LinkType.id == ltypeid).one()
                # copy node type to project
                newtype = ltype.copy_to_project(project)
                session.add(newtype)                
                # copy the link fields
                for field in ltype.fields:
                    try:
                        check_project_has_unit(session, project, \
                                           field.attribute_type.unit)
                    except AttributeError, exc:
                        pass
                    newfield = field.copy_to_otype(newtype)
                    session.add(newfield)
            session.commit()
            session.refresh(project)
            
            # FIXME : should update the fields that depend on the project
            
            # update user interface
            context = info.object.context
            if context is not None:
                try:
                    nodetype_menu =  context.application.get_nodetype_menu(context)
                    nodetype_menu.cleanup()
                    nodetype_menu.load_from_project(project)
                except AttributeError, exception:
                    logging.warn("No way to subscribe to the nodetype menu : %s", \
                             exception)

        return super(ButtonHandler, self).close(info, is_ok)


class ChooseProject(HasTraits):
    '''
    ChooseProject is a simple traits objects managing the selection of 
    a project that is not the one currently loaded
    '''
    session = Instance(Session)
    project = Instance(Project)
    projectid = Int(label="Project :")
    projects = Dict
    projectslist = Property(List, depends_on="project")
    
    def _projectid_default(self):
        """
        Projectid defaults to the id of the first project
        """
        return  self.projectslist[0].id

    def _projects_default(self):
        '''
        Returns the project list without the loaded project
        '''
        plist = dict()
        for project in self.projectslist:
            plist[project.id] = project.name
        return plist
    
    @cached_property
    def _get_projectslist(self):
        """
        Returns the list of project for the project trait
        """
        return self.session.query(Project).filter(\
            Project.id != self.project.id).all() 
    
    def get_view(self):        
        '''
        Returns a view with a CheckListEditor based on teh self.projects
        attribute of the object
        '''
        return View(
            Label("Choose a project to import object from :"),
            Item("projectid", 
                 editor=EnumEditor(values=self.projects),
                 style="simple",
                 height=25),
            title = "Import from project",
            buttons = [OKButton, CancelButton],
            resizable = True)
    
class ProjectObjectList(HasTraits):
    '''
    This class holds a list of nodetypes id and a list of linktypes id
    '''
    context = Instance(Context)
    session = Instance(Session)
    project = Instance(Project)
    from_projectid = Int
    ntypes = List(Int, label="Node types")
    ltypes = List(Int, label="Link types")
    importable_ntypes = List
    importable_ltypes = List
            
    
    def _importable_ntypes_default(self):
        '''
        Default importable_ntypes are all the node types that does not belong 
        to this project and do not have a name like the node type names 
        existing in this project
        '''
        # select all the node types not belonging to this project
        dbtypes = self.session.query(NodeType).filter(
            and_(NodeType.project_id!=self.project.id,
                 NodeType.project_id == self.from_projectid)).all()
        # filter the ones that have a matching name in the project
        out = []
        for dbtype in dbtypes:
            has_typename = self.session.query(NodeType).filter(
                and_(NodeType.project_id == self.project.id,
                     NodeType.name==dbtype.name)).first()
            if has_typename is None:
                out.append((dbtype.id, u"%s : %s" % \
                            (dbtype.name, dbtype.description)))
        return out
                
    def _importable_ltypes_default(self):
        '''
        Default importable_ltypes are all the link types that does not belong 
        to this project and do not have a name like the link type names 
        existing in this project
        '''
        # select all the node types not belonging to this project
        dbtypes = self.session.query(LinkType).filter(
            and_(LinkType.project_id!=self.project.id,
                 LinkType.project_id == self.from_projectid)).all()
        # filter the ones that have a matching name in the project
        out = []
        for dbtype in dbtypes:
            has_typename = self.session.query(LinkType).filter(
                and_(LinkType.project_id == self.project.id,
                    LinkType.name        == dbtype.name)).first()
            if has_typename is None:
                out.append((dbtype.id, dbtype.name))
        return out
        
    
    def get_view(self):
        '''
        Returns a view with CheckListEditor dynamically fed by 
        ntypes and ltypes
        '''
        return View(
            Item("ntypes", 
                    editor=CheckListEditor(
                            values=self.importable_ntypes), 
                    style="custom"),
            Item("_"),
            Item("ltypes", 
                            editor=CheckListEditor(
                                values=self.importable_ltypes),
                            style="custom"),
                       buttons = [OKButton, CancelButton],
                       resizable=True,
                       handler = ButtonHandler(),
                       title = "Import object types")
    